package com.bootdo.app.common.utils;

import com.bootdo.app.config.WXConfig;

import java.security.cert.X509Certificate;
import javax.net.ssl.*;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.*;

/**
 * @auther: 作者: Along
 * @description: 微信支付工具类
 * @Date: created in 2020/12/10 16:42
 */
public class WXPayUtil {

    public static final String SIGN_NAME = "MD5";
    public static final String SIGN_CHARSET = "UTF-8";
    public static final String SALT = "&key=" + WXConfig.getWxPayKey();

    /**
     * 将请求参数转换成xml
     * @param requestParams
     * @return
     */
    public static String mapToXml(Map<String, Object> requestParams) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        final Set<Map.Entry<String, Object>> entries = requestParams.entrySet();
        final Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> next = iterator.next();
            sb.append("<" + next.getKey() + ">" + next.getValue() + "</" + next.getKey() + ">");
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * 将xml数据解析成java对象
     * @param clazz 需要解析成的java字节码对象
     * @param inputStream xml 数据流
     * @param <T> 泛型
     * @return 泛型约束的java对象
     */
    public static <T> T parseWeiXinXml(Class<T> clazz, InputStream inputStream) {
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            InputStreamReader reader = new InputStreamReader(inputStream, "UTF-8");
            return (T) unmarshaller.unmarshal(reader);
        } catch (JAXBException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 将xml数据解析成java对象
     * @param clazz 需要解析成的java字节码对象
     * @param s xml 数据
     * @param <T> 泛型
     * @return 泛型约束的java对象
     */
    public static <T> T parseWeiXinXml(Class<T> clazz, String s) {
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            StringReader stringReader = new StringReader(s);
            return (T) unmarshaller.unmarshal(stringReader);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 从请求参数中获取签名内容
     * @param params 请求参数
     * @return
     */
    public static String getSignContent(Map<String,Object> params) {
        if (params == null || params.isEmpty()) {
            return null;
        }
        StringBuffer sb = new StringBuffer();
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        int size = keys.size();
        for (int i = 0; i < size; i++) {
            String key = keys.get(i);
            String value = String.valueOf(params.get(key));
            if (StringUtils.isNotEmpty(key,value)) {
                sb.append((i == 0 ? "" : "&") + key + "=" + value);
            }
        }
        return sb.toString();
    }

    /**
     * 从包含sign的参数中获取验签内容
     * @param params 请求参数
     * @return
     */
    public static String getSignCheckContent(Map<String,Object> params) {
        if (params == null || params.isEmpty()) {
            return null;
        }
        params.remove("sign");
        StringBuffer sb = new StringBuffer();
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
        int size = keys.size();
        for (int i = 0; i < size; i++) {
            String key = keys.get(i);
            String value = String.valueOf(params.get(key));
            if (StringUtils.isNotEmpty(key,value)) {
                sb.append((i == 0 ? "" : "&") + key + "=" + value);
            }
        }
        return sb.toString();
    }

    /**
     * md5签名
     * @param params 请求参数
     * @param salt 盐值
     * @param charset 字符集编码
     * @return
     */
    public static String md5ContentSign(Map<String,Object> params,String salt,String charset){
        String content = getSignContent(params);
        return md5ContentSign(content,salt,charset);
    }

    /**
     * md5 签名
     * @param content 待签名内容
     * @param salt 盐值
     * @param charset 字符集编码
     * @return
     */
    public static String md5ContentSign(String content,String salt,String charset){
        try{
            MessageDigest md = MessageDigest.getInstance(SIGN_NAME);
            content = content + salt;
            if(charset == null || "".equals(charset.trim()) || "null".equalsIgnoreCase(charset.trim())){
                md.update(content.getBytes());
            }else{
                md.update(content.getBytes(charset));
            }
            byte[] digest = md.digest();
            StringBuffer sb = new StringBuffer();
            int b;
            for(int i = 0; i < digest.length; i++){
                b = digest[i];
                if(b < 0){
                    b += 256;
                }
                if(b < 16){
                    sb.append("0");
                }
                sb.append(Integer.toHexString(b));
            }
            return sb.toString().toUpperCase();
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * md5 验签
     * @param content 签名数据
     * @param salt 盐值
     * @param sign 签名
     * @param charset 字符集编码
     * @return
     */
    public static boolean md5CheckContent(String content,String salt,String sign,String charset) {
        try {
            content = content + salt;
            MessageDigest md = MessageDigest.getInstance(SIGN_NAME);
            if (charset == null || "".equals(charset.trim()) || "null".equalsIgnoreCase(charset.trim())) {
                md.update(content.getBytes());
            } else {
                md.update(content.getBytes(charset));
            }
            byte b[] = md.digest();
            int i;
            StringBuffer buf = new StringBuffer();
            for (int offset = 0; offset < b.length; offset++) {
                i = b[offset];
                if (i < 0){
                    i += 256;
                }
                if(i < 16){
                    buf.append("0");
                }
                buf.append(Integer.toHexString(i));
            }
            if (buf.toString().toUpperCase().equals(sign)) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 移除 sign & signName 用UTF-8编码验签
     * @param params 请求参数
     * @param salt 盐值
     * @return
     */
    public static boolean md5CheckUTF8(Map<String, Object> params, String salt)  {
        String sign = String.valueOf(params.get("sign"));
        String content = getSignCheckContent(params);
        return md5CheckContent(content,salt,sign,SIGN_CHARSET);
    }

    /**
     * 加载证书密钥
     * @param type 证书格式 pkcs12
     * @param filePath 证书路径
     * @param password 证书密码
     * @return
     */
    public static KeyStore loadKeyStore(String type,String filePath,String password){
        KeyStore keyStore = null;
        try {
            keyStore = KeyStore.getInstance(type);
            FileInputStream fileInputStream = new FileInputStream(filePath);
            keyStore.load(fileInputStream,password.toCharArray());
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return keyStore;
    }

    /**
     * 获取 SSL 套接字工厂
     * @param keyStore 密钥证书
     * @param password 证书密码
     * @return
     */
    public static SSLSocketFactory getSSLFactory(KeyStore keyStore,String password){
        SSLSocketFactory socketFactory = null;
        if(keyStore != null){
            try {
//                SSLContext sslContext = SSLContext.getInstance("TLS");
                SSLContext sslContext = SSLContext.getInstance("SSL");
                // 密钥内容源的密钥管理器的工厂
                KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
                keyManagerFactory.init(keyStore, password.toCharArray());
                // 信任材料源的信任管理器的工厂
//                TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
//                trustManagerFactory.init(keyStore);
                // 第一个参数是授权的密钥管理器，用来授权验证。TrustManager[]第二个是被授权的证书管理器，用来验证服务器端的证书。第三个参数是一个随机数值，可以填写null
//                sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());

                X509TrustManager tm = new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] arg0,String arg1) throws CertificateException {
                    }
                    @Override
                    public void checkServerTrusted(X509Certificate[] arg0,String arg1) throws CertificateException {
                    }
                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
                };
                sslContext.init(keyManagerFactory.getKeyManagers(), new TrustManager[]{tm}, new SecureRandom());
                socketFactory = sslContext.getSocketFactory();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (KeyStoreException e) {
                e.printStackTrace();
            } catch (UnrecoverableKeyException e) {
                e.printStackTrace();
            } catch (KeyManagementException e) {
                e.printStackTrace();
            }
        }
        return socketFactory;
    }

}
